home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Windows Expert
/
Windows Expert.iso
/
desktop
/
todo_102.zip
/
FM_UTL11.ZIP
/
FM_UTILS.C
next >
Wrap
C/C++ Source or Header
|
1993-03-02
|
23KB
|
720 lines
//***************************************************************************
//
// Library:
// FM_UTILS.DLL - version 1.1a
//
// Purpose:
// FM_UTILS is a File Manager extension DLL. An extension DLL adds
// a menu to File Manager, contains an entry point that processes menu
// commands and notification messages sent by File Manager, and
// queries data and information about the File Manager windows. The
// purpose of an extension DLL is to add administration support
// features to File Manager, for example, file and disk utilities.
// Up to five extension DLLs may be installed at any one time.
//
// FM_UTILS adds a menu (called "Utilities" by default) to File Manager
// and processes all the messages that are sent by File Manager to an
// extension DLL. In order to retrieve any information, it sends
// messages to File Manager.
//
// Usage:
// File Manager installs the extensions that have entries in the
// [AddOns] section of the WINFILE.INI initialization file. An entry
// consists of a tag and a value. To load FM_UTILS.DLL as a File
// Manager extension, add the following to WINFILE.INI (assuming the
// DLL resides in c:\win\system):
//
// [AddOns]
// FM_Utils Extension=fm_utils.dll
//
// In addition, a section specific to this Extension DLL is added to
// WINFILE.INI. There are several elements to this section:
// For each utility to appear on the menu, there is a 'UserProgn=' tag,
// and a 'UserDescn=' tag. Comments (preceded by semicolons) are optional.
// You may have up to twenty separate utilities specified.
// You can also specify up to five utilities to load at the same time as
// FileMan, and exit when you close FileMan. You use the 'PreLoadn='
// tag for this. All of the utilities are started with the SW_SHOWMINNOACTIVE
// setting (minimized, but not activated).
// And, you can specify the name of the added menu. If you choose not to,
// the default is "&Utilities". This provides foreign language support,
// as well as allowing you to use a very short name, to keep the menu bar
// from growing too long. The tag to use is 'UserMenuName='.
//
// [FM_Utils Extension]
// ;Name the menu
// UserMenuName=&Util
// ;add Upper Deck Editor to menu
// UserDesc1=&Edit File(s)
// UserProg1=D:\UDE\UDE.EXE
// ;add V. Buerg's LIST to menu
// UserDesc2=&View File(s)
// UserProg2=LIST.PIF
// ;PreLoad Trash Can
// PreLoad1=TRASH.EXE
//
// Revision History:
// 1.0 - March '92 - initial release
//
// 1.1 - April '92 - added ability to PreLoad utilities. This seems even
// more popular than the original DLL.
//
// 1.1a - August '92 - added ability to specify menu name; changed
// SendMessage(...WM_CLOSE...) to PostMessage(...WM_CLOSE...)
// since it seemed to make more sense.
//
// Notes:
// Portions of the base code for this utility is based on, and borrowed
// from, the XTENSION sample code provided with the Microsoft Windows
// 3.1 SDK.
//
// FM_UTILS attempts to close any auto-started utilities by posting a
// WM_CLOSE to it's main window. There's no guarantee that this method
// will work for *all* programs. If you know of a better way to handle
// this situation, I'd be *really* happy to hear about it.
//
// While it's not properly documented anywhere, it seems that the maximum
// command line to pass to WinExec() is 128 characters. Since we have to
// pass in complete pathnames for all files in order for this to work,
// we may not be able to pass *all* selected file names.
//
// Copyright (c) 1992 by Brad P. Smith - all rights reserved
//
// snail: R.R. #2 - 777 Crozier Rd.
// Oxford Mills, Ontario, CANADA
// K0G 1S0
// E-Mail: 'B.P.Smith' on BIX, or 'B.P.Smith@BIX.com' on Internet
//
// This utility DLL is FreeWare. Don't let *anybody* charge you
// for it.
// However, I would *really* appreciate hearing from you if you find
// FM_UTILS useful. Drop me a postcard or E-mail. I could use the moral
// support. :-)
//
//***************************************************************************
#include <windows.h>
#include <string.h>
#include <direct.h>
#include <wfext.h>
#include "fm_utils.h"
//******************** Global Variables
HANDLE ghDllInst; // DLL's instance handle
HMENU ghMenu; // Extension's menu handle
WORD gwMenuDelta; // Delta for extension's menu items
USERPROG UserProg[ NUM_USERPROGS ];
PRELOADPROG PreLoadProg[ NUM_PRELOADPROGS ];
char szTempBuffer[ PATH_NAME_LEN ];
//***************************************************************************
//
// LibMain()
//
// Purpose:
// LibMain is called by LibEntry. LibEntry is called by Windows
// when the DLL is loaded. The LibEntry routine is provided
// in the LIBENTRY.OBJ in the SDK Link Libraries disk. (The
// source LIBENTRY.ASM is also provided.)
//
// LibEntry initializes the DLL's heap if a HEAPSIZE value is
// specified in the DLL's DEF file. After this, LibEntry calls
// LibMain. The LibMain function below satisfies that call.
//
// The LibMain function should perform additional initialization
// tasks required by the DLL. In this DLL, no initialization
// tasks are required; only the DLL's instance handle is saved.
// LibMain should return a value of TRUE if the initialization is
// successful.
//
// Parameters:
// hLibInst - DLLs instance handle
// wDataSeg - Data segment
// cbHeapSize - Size of the DLL's heap
// lpszCmdLine - Command line
//
// Return Value:
// TRUE if the initialization is successful; FALSE otherwise.
//
//***************************************************************************
int CALLBACK LibMain( HANDLE hLibInst, WORD wDataSeg,
WORD cbHeapSize, LPSTR lpszCmdLine )
{
ghDllInst = hLibInst;
return TRUE;
}
//***************************************************************************
//
// WEP()
//
// Purpose:
// Performs cleanup tasks when the .DLL is unloaded. The WEP() is
// called automatically by Windows when the DLL is unloaded.
//
// Make sure that the WEP() is @1 RESIDENTNAME in the EXPORTS
// section of the .DEF file. This ensures that the WEP() can
// be called as quickly as possible. Incidently, this is why
// the WEP() is called the WEP() instead of WindowsExitProcedure().
// It takes up the minimum amount of space and is quickly located.
//
// Parameters:
// bSystemExit - Type of exit
//
// Return Value:
// TRUE.
//
//***************************************************************************
int CALLBACK WEP( int bSystemExit )
{
return TRUE;
}
//***************************************************************************
//
// FMExtensionProc()
//
// Purpose:
// This is an application-defined callback function. It processes menu
// commands and messages sent to FM_UTILS.DLL.
//
// Parameters:
// hWndExtension - Identifies the File Manager window
// wMessage - Message sent to extension DLL
// lParam - Message information
//
// Return Value:
// When the wMessage is FMEVENT_LOAD, the handle to extension's menu
// should be returned; otherwise a NULL value.
//
//***************************************************************************
HMENU CALLBACK FMExtensionProc( HWND hWndExtension, WORD wMessage, LONG lParam )
{
LPFMS_LOAD lpload;
FARPROC lpDialogProc;
switch( wMessage )
{
case FMEVENT_LOAD:
lpload = (LPFMS_LOAD) lParam;
// Assign the menu handle from the DLL's resource
ghMenu = LoadMenu( ghDllInst, "UtilsExtensionMenu" );
lpload->hMenu = ghMenu;
// This is the delta we are being assigned.
gwMenuDelta = lpload->wMenuDelta;
lpload->dwSize = sizeof( FMS_LOAD );
// Assign the popup menu name for this extension
NameMenu( lpload->szMenuName );
InitializeMenu( ghMenu );
PreLoadPrograms();
return ghMenu;
// Kill any utilities we started, and free up allocated memory
case FMEVENT_UNLOAD:
KillPreLoadedPrograms();
CleanUpMenu();
break;
case IDM_ABOUT:
lpDialogProc = (FARPROC)AboutDlgProc;
DialogBoxParam( ghDllInst, "About", hWndExtension,
lpDialogProc, (LONG)hWndExtension );
break;
default:
// If one of our menu items chosen, process it, otherwise ignore
if( wMessage >= IDM_USERPROG && wMessage <= IDM_MAXUSERPROG )
UserProgram( wMessage-IDM_USERPROG, hWndExtension );
break;
}
return NULL;
}
//***************************************************************************
//
// NameMenu()
//
// Purpose:
// We parse WINFILE.INI, looking for an entry 'UserMenuName='. If found
// we use *that* name for our added menu, otherwise we use the default
// "&Utilities"
//
// Parameters:
// szName - the buffer to put the Menu Name into.
//
// Return Value:
// none.
//
// Notes:
// We don't perform *any* validity checking on the string, so it is quite
// possible for the user to specify "&File" (for example) and totally
// confuse him/herself (but not Windows, fortunately).
//
//***************************************************************************
void NameMenu( LPSTR szName )
{
GetPrivateProfileString( (LPSTR)"FM_Utils Extension",
(LPSTR)"UserMenuName", (LPSTR)"&Utilities", szName, MENU_TEXT_LEN,
(LPSTR)"WINFILE.INI" );
}
//***************************************************************************
//
// InitializeMenu()
//
// Purpose:
// We parse WINFILE.INI, looking for the list of User Programs and
// descriptions to add to our menu. To save time, we dynamically
// allocate buffers to store the program names & descriptions.
// As we find each complete entry, we add it to the menu.
//
// Parameters:
// none.
//
// Return Value:
// none.
//
// Notes:
// We really don't *need* to store the Description, because it's of
// little use to us once it's added to the menu. However, we may
// someday find use for it, so we may as well keep it.
//
//***************************************************************************
int InitializeMenu( HMENU hMenu )
{
WORD wProgNum;
int iNumItems = 0;
char szUserProgEntry[ ENTRY_TAG_LENGTH ];
for( wProgNum = 0; wProgNum < NUM_USERPROGS; ++wProgNum )
{
UserProg[wProgNum].fActive = FALSE;
// Get the Utility's path
wsprintf( szUserProgEntry, "UserProg%u", wProgNum+1 );
GetPrivateProfileString( (LPSTR)"FM_Utils Extension",
(LPSTR)szUserProgEntry, "", szTempBuffer, PATH_NAME_LEN,
(LPSTR)"WINFILE.INI" );
if( !( strlen( szTempBuffer )))
break;
else
{
UserProg[wProgNum].szPath=(NPSTR)LocalAlloc( LPTR, strlen( szTempBuffer )+1 );
if( !UserProg[wProgNum].szPath )
{
MessageBox( NULL, "Memory Allocation Error\nCreating Extension Menu",
"File Manager", MB_ICONEXCLAMATION | MB_OK );
break;
}
strcpy( UserProg[wProgNum].szPath, szTempBuffer );
}
// Get the Description to add to the menu
wsprintf( szUserProgEntry, "UserDesc%u", wProgNum+1 );
GetPrivateProfileString( (LPSTR)"FM_Utils Extension",
(LPSTR)szUserProgEntry, "", szTempBuffer, PROG_DESC_LENGTH,
(LPSTR)"WINFILE.INI" );
if( !( strlen( szTempBuffer )))
{
LocalFree( (HLOCAL)UserProg[wProgNum].szPath );
break;
}
else
{
UserProg[wProgNum].szDescription=(NPSTR)LocalAlloc( LPTR, strlen( szTempBuffer )+1 );
if( !UserProg[wProgNum].szDescription )
{
LocalFree( (HLOCAL)UserProg[wProgNum].szPath );
MessageBox( NULL, "Memory Allocation Error\nCreating Extension Menu",
"File Manager", MB_ICONEXCLAMATION | MB_OK );
break;
}
strcpy( UserProg[wProgNum].szDescription, szTempBuffer );
}
// If all is okay, prepare to add this item to the menu
UserProg[wProgNum].fActive = TRUE;
AppendMenu( hMenu, MF_STRING | MF_ENABLED,
IDM_USERPROG+wProgNum, (LPSTR)UserProg[wProgNum].szDescription );
++iNumItems;
}
return iNumItems;
}
//***************************************************************************
//
// CleanUpMenu()
//
// Purpose:
// To be nice folks, we go through and free up all of our allocated
// memory.
//
// Parameters:
// none.
//
// Return Value:
// none.
//
//***************************************************************************
void CleanUpMenu()
{
WORD wProgNum;
for( wProgNum = 0; wProgNum < NUM_USERPROGS; ++wProgNum )
{
if( !UserProg[wProgNum].fActive )
break;
LocalFree( (HLOCAL)UserProg[wProgNum].szPath );
LocalFree( (HLOCAL)UserProg[wProgNum].szDescription );
UserProg[wProgNum].fActive = FALSE;
}
}
//***************************************************************************
//
// PreLoadPrograms()
//
// Purpose:
// Parse the name of a utility to run, and attempt to start it using
// WinExec(). If we're successful, we determine and keep the window
// handle of the main WndProc for that program. We'll need that later to
// close the program.
//
// Parameters:
// none.
//
// Return Value:
// none.
//
//***************************************************************************
void PreLoadPrograms()
{
WORD wProgNum;
char szPreLoadProgEntry[ ENTRY_TAG_LENGTH ];
for( wProgNum = 0; wProgNum < NUM_PRELOADPROGS; ++wProgNum )
{
PreLoadProg[wProgNum].fActive = FALSE;
// Get the Utility's path
wsprintf( szPreLoadProgEntry, "PreLoad%u", wProgNum+1 );
GetPrivateProfileString( (LPSTR)"FM_Utils Extension",
(LPSTR)szPreLoadProgEntry, "", szTempBuffer, PATH_NAME_LEN,
(LPSTR)"WINFILE.INI" );
if( !( strlen( szTempBuffer )))
break;
else
{
PreLoadProg[wProgNum].szPath=(NPSTR)LocalAlloc( LPTR, strlen( szTempBuffer )+1 );
if( !PreLoadProg[wProgNum].szPath )
{
MessageBox( NULL, "Memory Allocation Error\nPre-Loading Programs",
"File Manager", MB_ICONEXCLAMATION | MB_OK );
break;
}
strcpy( PreLoadProg[wProgNum].szPath, szTempBuffer );
}
// If all is okay, attempt start the program
PreLoadProg[wProgNum].hInst =
WinExec( (LPSTR)PreLoadProg[wProgNum].szPath, SW_SHOWMINNOACTIVE );
// If we failed, don't bother to notify. Otherwise, determine the handle
if( PreLoadProg[wProgNum].hInst >= 32 )
{
PreLoadProg[wProgNum].fActive = TRUE;
PreLoadProg[wProgNum].hWnd =
DetermineProgramHandle( PreLoadProg[wProgNum].hInst );
}
}
}
//***************************************************************************
//
// DetermineProgramHandle()
//
// Purpose:
// Given the Instance Handle of a program, we want to find out the Window
// handle of its main WndProc.
//
// Parameters:
// hInst - Instance handle of the app we're trying to find the handle of.
//
// Return Value:
// hWnd - Window handle of app's main WndProc
//
//***************************************************************************
HWND DetermineProgramHandle( HINSTANCE hInst )
{
HWND hWnd;
HWND hWndParent;
// Find the first top-level window that matches our selected instance handle.
for( hWnd = GetWindow( GetDesktopWindow(), GW_CHILD );
hWnd;
hWnd = GetWindow( hWnd, GW_HWNDNEXT ) )
{
if( GetWindowWord( hWnd, GWW_HINSTANCE ) == hInst )
break;
}
// Iconized programs can have *two* top-level window handles, the icon
// (or main window), and the icon title.
// In case we didn't get the top-most window of this task, loop through
// until GetParent returns NULL. Then, we have the window we want to send
// our WM_CLOSE to.
while( hWndParent = GetParent( hWnd ) )
hWnd = hWndParent;
return hWnd;
}
//***************************************************************************
//
// KillPreLoadedPrograms()
//
// Purpose:
// We step through the window handles of the preloaded apps, and send
// WM_CLOSE messages to them. Naturally, we check first to ensure that
// the window handle is still valid.
//
// Parameters:
// none.
//
// Return Value:
// none.
//
// Notes:
// We have no guarantee that, when the main WndProc of the application
// receives a WM_CLOSE message, that it will actually terminate the
// app. If somebody knows of a better way to handle this, I'd be *real*
// happy to hear about it.
//
//***************************************************************************
void KillPreLoadedPrograms()
{
WORD wProgNum;
for( wProgNum = 0; wProgNum < NUM_PRELOADPROGS; ++wProgNum )
{
if( !PreLoadProg[wProgNum].fActive )
break;
// Check to make sure the Window is still active, and still matches
// the instance handle. If so, attempt to close it.
if(( IsWindow( PreLoadProg[wProgNum].hWnd )) &&
( GetWindowWord( PreLoadProg[wProgNum].hWnd, GWW_HINSTANCE ) ==
PreLoadProg[wProgNum].hInst ))
{
PostMessage( PreLoadProg[wProgNum].hWnd, WM_CLOSE, 0, 0L );
}
LocalFree( (HLOCAL)PreLoadProg[wProgNum].szPath );
PreLoadProg[wProgNum].fActive = FALSE;
}
}
//***************************************************************************
//
// UserProgram()
//
// Purpose:
// This is where we select which User Program has been selected to
// run. If there are one or more files selected, we generate a command
// line, and then execute it using WinExec().
//
// Parameters:
// wProgNum - Identifies the index of the user program to run
// hWndExtension - Identifies the File Manager window
//
// Return Value:
// none.
//
// Notes:
// While it doesn't seem to be specifically documented anywhere, the
// maximum number of characters in a command line can't exceed 128.
// Since it's possible to select any number of files, it's pretty easy
// to blow this limit. In order to maximize our use of this limited
// space, we perform some trickery.
// Since we know that File Manager (currently) doesn't allow selection
// of multiple files across different directories or drives, we know
// that the path portion of all selected files should be the same. So,
// we determine the path portion, and actually make that directory our
// current directory, and chop the path portion from all of the selected
// files (WinExec() will search for the utility, if its full path is
// not specified, using the standard Windows search mechanism).
// If we still try to blow the length limit, a warning MessageBox will
// pop up, and the command line will be truncated at the last possble
// file addition. While actually *changing* the file selections in
// File Manager, it looks like this isn't feasible.
//
//***************************************************************************
void UserProgram( WORD wProgNum, HWND hWndExtension )
{
static FMS_GETFILESEL fmsFileInfo;
NPSTR szCmdLine;
WORD wSelFileCount;
WORD wProgIndex;
WORD wWinExecReturn;
WORD wCmdLineLength;
char *szFileName;
if( !UserProg[wProgNum].fActive )
return;
wSelFileCount = (WORD)SendMessage( hWndExtension, FM_GETSELCOUNTLFN, 0, 0L );
// if( !wSelFileCount )
// {
// MessageBox( NULL, "No Files Selected!", "File Manager",
// MB_ICONEXCLAMATION | MB_OK );
// return;
// }
szCmdLine = (NPSTR)LocalAlloc( LPTR, strlen( UserProg[wProgNum].szPath )+1 );
if( !szCmdLine )
{
MessageBox( NULL, "Unable to create command line!", "File Manager",
MB_ICONEXCLAMATION | MB_OK );
return;
}
strcpy( szCmdLine, UserProg[wProgNum].szPath );
for( wProgIndex = 0; wProgIndex < wSelFileCount; wProgIndex++ )
{
SendMessage( hWndExtension, FM_GETFILESELLFN, wProgIndex,
(LONG)(LPFMS_GETFILESEL)&fmsFileInfo );
// We know that all of the selected files will be in the same directory,
// because File Manager doesn't allow selection across drives or
// directories. We can use this to our advantage by determining the
// selected directory, making it the current directory, and only putting
// the actual file names on the command line. This maximizes our use of
// the command line (which, remember, is limited to 128 characters).
if( wProgIndex == 0 )
{
strcpy( szTempBuffer, fmsFileInfo.szName );
szFileName = strrchr( szTempBuffer, '\\' );
// We don't chop the trailing '\' on root directory
if( (WORD)( szFileName - szTempBuffer ) > 3 )
szFileName[0] = 0;
else
szFileName[1] = 0;
_chdrive( szTempBuffer[0] - 'A' + 1 );
_chdir( szTempBuffer );
// OutputDebugString( (LPSTR)szTempBuffer );
// OutputDebugString( (LPSTR)"\n" );
}
szFileName = (char *)( strrchr( fmsFileInfo.szName, '\\' ) + 1 );
wCmdLineLength = strlen( szCmdLine ) + strlen( szFileName ) + 2;
if( wCmdLineLength <= MAX_CMDLINE_LENGTH )
{
szCmdLine = (NPSTR)LocalReAlloc( (HLOCAL)szCmdLine,
wCmdLineLength, LMEM_ZEROINIT );
if( !szCmdLine )
{
LocalFree( (HLOCAL)szCmdLine );
MessageBox( NULL, "Unable to construct command line!", "File Manager",
MB_ICONEXCLAMATION | MB_OK );
return;
}
strcat( szCmdLine, " " );
strcat( szCmdLine, szFileName );
}
else
{
wsprintf( szTempBuffer,
"Adding file path\n%s\nwould make command line too long.\nTruncating command line.",
(LPSTR)fmsFileInfo.szName );
MessageBox( NULL, szTempBuffer, "File Manager", MB_ICONEXCLAMATION | MB_OK );
break;
}
}
// OutputDebugString( (LPSTR)szCmdLine );
// OutputDebugString( (LPSTR)"\n" );
wWinExecReturn = WinExec( szCmdLine, SW_SHOWNORMAL );
LocalFree( (HLOCAL)szCmdLine );
if( wWinExecReturn < 32 )
{
wsprintf( szTempBuffer, "Unable to Execute\n%s", UserProg[wProgNum].szPath );
MessageBox( NULL, szTempBuffer, "File Manager", MB_ICONEXCLAMATION | MB_OK );
}
}
//***************************************************************************
//
// AboutDlgProc()
//
// Purpose:
// Your typical "About" box, in case you forget who wrote this amazing
// piece of code. :-)
//
// Parameters:
// The usual DLGPROC parameters
//
// Return Value:
// The typical DLGPROC BOOL return
//
//***************************************************************************
BOOL CALLBACK AboutDlgProc( HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam )
{
switch( wMsg )
{
case WM_COMMAND:
switch( wParam )
{
case IDOK:
case IDCANCEL:
EndDialog( hDlg, TRUE );
break;
default:
return FALSE;
}
return TRUE;
default:
break;
}
return FALSE;
}